﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using iNet.Media.Imaging.Png;
using iNet.Media.Xmp;

namespace iNet.Media.Imaging
{
	/// <summary>
	/// PNG 影像解碼工作。
	/// </summary>
	public class PngImageDecodeTask : BasicImageDecodeTask
	{
		#region 建構子
		/// <summary>
		/// 初始化新的 PngImageDecodeTask 執行個體。
		/// </summary>
		public PngImageDecodeTask()
			: base(ImageFormats.Png)
		{ }
		/// <summary>
		/// 初始化新的 PngImageDecodeTask 執行個體。
		/// </summary>
		/// <param name="source">影像儲存來源。</param>
		public PngImageDecodeTask(MediaStorage source)
			: base(ImageFormats.Png, source, 0, 0)
		{ }
		#endregion
		#region DecodeMetadataOverride
		internal override IMetadata DecodeMetadataOverride(Stream stream, bool isReadOnly)
		{
			//讀取標頭
			byte[] buffer8 = new byte[8];
			if (stream.CanSeek)
				stream.Seek(8, SeekOrigin.Current);
			else
			{
				if (stream.Read(buffer8, 0, 8) < 8)
					throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
			}

			//讀取中繼資料
			BitConverter bitConverter = BitConverter.BigEndian;
			List<TextualMetadataItem> textualItems = new List<TextualMetadataItem>();
			List<XmpProperty> xmpProperties = new List<XmpProperty>();
			bool read = true;
			while (read)
			{
				//取得 Chunk 類型
				if (stream.Read(buffer8, 0, 8) < 8)
					break;
				PngChunkType chunkType = (PngChunkType)bitConverter.ToUInt32(buffer8, 4);

				//讀取中繼資料
				switch (chunkType)
				{
					case PngChunkType.tEXt:
					case PngChunkType.iTXt:
					case PngChunkType.zTXt:
						{
							stream.Seek(-8, SeekOrigin.Current);
							PngChunk chunk = new PngChunk(stream);
							this.PngChunkToMetadata(chunk, textualItems, xmpProperties);
						}
						break;
					case PngChunkType.IEND:
						read = false;
						break;
					default:
						{
							long length = bitConverter.ToUInt32(buffer8) + 4;
							stream.Seek(length, SeekOrigin.Current);
						}
						break;
				}
			}

			//回傳
			return new PngMetadata(textualItems, xmpProperties, isReadOnly);
		}
		#endregion
		#region DecodePixelSize
		internal override Size<int> DecodePixelSize(Stream stream)
		{
			//讀取標頭
			if (stream.CanSeek)
				stream.Seek(8, SeekOrigin.Current);
			else
			{
				byte[] buffer = new byte[8];
				if (stream.Read(buffer, 0, 8) < 8)
					throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
			}

			//取得影像大小
			while (true)
			{
				//取得 chunk
				PngChunk chunk = new PngChunk(stream);

				//取得影像大小
				switch (chunk.Type)
				{
					case PngChunkType.IHDR:
						return new Size<int>(BitConverter.BigEndian.ToInt32(chunk.Data, 0), BitConverter.BigEndian.ToInt32(chunk.Data, 4));
					case PngChunkType.IDAT:
					case PngChunkType.IEND:
						throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat);
				}
			}
		}
		#endregion
		#region PngChunkToMetadata
		void PngChunkToMetadata(PngChunk chunk, List<TextualMetadataItem> textualItems, List<XmpProperty> xmpProperties)
		{
			//確認
			if (chunk.Data == null)
				return;

			//搜尋分隔字元
			int index = 0;
			byte[] data = chunk.Data;
			while (data[index] != '\0' && index < data.Length)
				++index;
			if (index == 0)
				return;

			//取得 keyword
			string keyword = Encoding.Default.GetString(data, 0, index);

			//取得字串
			string value;
			if (chunk.Type != PngChunkType.zTXt)
			{
				if (index >= data.Length - 1)
					value = "";
				else if (keyword == "XML:com.adobe.xmp")			//===== XMP =====
				{
					//取出 XMP 資料
					index += 5;
					if (index >= data.Length)
						return;
					byte[] buffer = new byte[data.Length - index];
					Array.Copy(data, index, buffer, 0, buffer.Length);

					//取得 XMP 中繼資料
					using (MemoryStream mStream = new MemoryStream(buffer))
					{
						using (XmpPropertyEnumerator enumerator = new XmpPropertyEnumerator(mStream))
						{
							while (enumerator.MoveNext())
								xmpProperties.Add(enumerator.Current);
						}
					}

					//完成
					return;
				}
				else												//===== 一般字串 =====
					value = Encoding.Default.GetString(data, index + 1, data.Length - index - 1);
			}
			else													//===== 壓縮字串 =====
			{
				return;
				/*
				if (index >= data.Length - 2 || data[index + 1] != 0)
					value = "";
				else
				{
					using (MemoryStream mstream = new MemoryStream(data))
					{
						mstream.Position = index + 2;
						using (DeflateStream dstream = new DeflateStream(mstream, CompressionMode.Decompress))
							data = ReadBytesFromDeflateStream(dstream);
						value = Encoding.Default.GetString(data);
					}
				}
				*/
			}

			//加入集合
			textualItems.Add(new TextualMetadataItem(keyword, value));
		}
		#endregion
		#region StringToDateTime
		object StringToDateTime(string value)
		{
			StringBuilder buffer = new StringBuilder(value);
			DateTime dateTime;
			int count = 0;
			for (int i = 0; i < buffer.Length && count < 2; i++)
			{
				if (buffer[i] == ':')
				{
					buffer[i] = '/';
					count++;
				}
			}
			if (count < 2 || !DateTime.TryParse(buffer.ToString(), out dateTime))
				return value;
			return dateTime;
		}
		#endregion
	}
}
